home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cocktail / doc.lha / doc.doc / ell.doc < prev    next >
Text File  |  1992-09-25  |  44KB  |  1,356 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11. ___________________________________________________________________
  12.  
  13.  
  14.                                    Efficient and Comfortable
  15.                                    Error Recovery in
  16.                                    Recursive Descent Parsers
  17.  
  18.                                    J. Grosch
  19.  
  20. ___________________________________________________________________
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50. ___________________________________________________________________
  51.                                    GESELLSCHAFT FUeR MATHEMATIK
  52.                                    UND DATENVERARBEITUNG MBH
  53.  
  54.                                    FORSCHUNGSSTELLE FUeR
  55.                                    PROGRAMMSTRUKTUREN
  56.                                    AN DER UNIVERSITAeT KARLSRUHE
  57. ___________________________________________________________________
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.                                    Project
  76.  
  77.                              Compiler Generation
  78.  
  79.          ___________________________________________________________
  80.  
  81.                    Efficient and Comfortable Error Recovery
  82.                          in Recursive Descent Parsers
  83.  
  84.  
  85.                                  Josef Grosch
  86.  
  87.  
  88.                                 Dec. 11, 1989
  89.  
  90.          ___________________________________________________________
  91.  
  92.  
  93.                                 Report No. 19
  94.  
  95.  
  96.                              Copyright c 1989 GMD
  97.  
  98.  
  99.             Gesellschaft fuer Mathematik und Datenverarbeitung mbH
  100.                 Forschungsstelle an der Universitaet Karlsruhe
  101.                           Vincenz-Priesznitz-Str. 1
  102.                                D-7500 Karlsruhe
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.                                                                              1
  135.  
  136.  
  137.     Efficient and Comfortable Error Recovery in Recursive Descent Parsers
  138.  
  139.                                  Josef Grosch
  140.               GMD Forschungsstelle an der Universitaet Karlsruhe
  141.              Vincenz-Priesznitz-Str. 1, D-7500 Karlsruhe, Germany
  142.                               Tel: +721-6622-26
  143.                        E-Mail: grosch@karlsruhe.gmd.de
  144.  
  145.  
  146. Abstract
  147.  
  148.      This paper describes the interesting features of  the  recursive  descent
  149. parser  generator  Ell  from the user's point of view. Some of the interesting
  150. implementation aspects are discussed. The generated parsers are extremely fast
  151. and  run at speed of 55,000 tokens per second or 900,000 lines per minute on a
  152. MC 68020 processor. The outstanding features  of  Ell  are  the  L-attribution
  153. mechanism, its ability to handle non LL(1) grammars, and its comfortable error
  154. handling, which includes error reporting, recovery, and repair.
  155.  
  156. 1.  Introduction
  157.  
  158.      Recursive descent parsing is an established technique for the analysis of
  159. LL(1)  languages  since  many  years.  It can be implemented easily by a hand-
  160. written program or by using one of the  existing  parser  generators  such  as
  161. [DuW81,  Gra88a, ReM85].  In real life applications, issues such as good qual-
  162. ity error recovery and high run time performance are essential. These require-
  163. ments are rarely discussed in the literature and seldom achieved by the exist-
  164. ing parser generators. This explains the reasons for implementing yet  another
  165. LL(1) parser generator and to describe some details of the generated code.
  166.  
  167.      This paper presents how comfortable features like error  recovery,  error
  168. repair, and L-attribution are implemented in the high performance parsers gen-
  169. erated by the parser generator Ell [Gro88, GrV88].   Ell  generates  recursive
  170. descent  parsers  from LL(1) grammars given in extended BNF. The grammar rules
  171. may be associated with semantic actions  consisting  of  arbitrary  statements
  172. which  are  executed whenever they are passed during a left-to-right parse. An
  173. L-attribution may be evaluated during parsing.  Ell  offers  possibilities  to
  174. resolve  the  LL(1)  conflicts  of  non LL(1) grammars.  The generated parsers
  175. automatically include error reporting, recovery, and repair.  Parsers  can  be
  176. generated  in  the  target languages C and Modula-2. The parsers are extremely
  177. fast and run at speed of 55,000 tokens per second or 900,000 lines per  minute
  178. on a MC 68020 processor.
  179.  
  180.      This paper addresses only the interesting features of Ell.  The mechanism
  181. for  L-attribution  and the error recovery are described from the user's point
  182. of view as well as from the implementation view. We  also  present  how  LL(1)
  183. conflicts are resolved and discuss further implementation issues such as test-
  184. ing for set membership or the pitfalls of CASE (switch) statements. The  exam-
  185. ples  are  taken  from  a  parser for Modula-2 and also use Modula-2 as target
  186. language.
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.                                                                              2
  200.  
  201.  
  202. 2.  L-Attribution
  203.  
  204.      According to [Wil79] an attribute grammar which can be  evaluated  during
  205. LL(1)-parsing  is  called  an  L-attributed  grammar. The notion L-attribution
  206. means that all attributes can be evaluated in a single top-down  left-to-right
  207. tree walk.
  208.  
  209. 2.1.  User's View
  210.  
  211.      The specification language of Ell distinguishes three  kinds  of  grammar
  212. symbols: nonterminals, terminals, and literals. Literals are similar to termi-
  213. nals and are denoted by strings.  Terminals and nonterminals  are  denoted  by
  214. identifiers.  Terminals and nonterminals can be associated with arbitrary many
  215. attributes of arbitrary types. The computation of the attribute  values  takes
  216. place  in  the semantic action parts of a rule. The attributes are accessed by
  217. an attribute designator which consists of the name of the  grammar  symbol,  a
  218. dot  character, and the name of the attribute. As several grammar symbols with
  219. the same name can occur within a rule, the grammar symbols are denoted unambi-
  220. guously  by appending numbers to their names. The left-hand side symbol always
  221. receives the number zero. For every (outermost) alternative of the  right-hand
  222. side, the symbols with the same name are counted starting from one.
  223.  
  224.     Example:
  225.     expr    : ( [ '+' ] term { expr0.value :=   term1.value;                }
  226.               | '-' term     { expr0.value := - term2.value;                }
  227.               )
  228.               ( '+' term     { INC (expr0.value, term3.value);              }
  229.               | '-' term     { DEC (expr0.value, term4.value);              }
  230.               ) *
  231.             .
  232.     term    : fact           { term0.value := fact1.value;                  }
  233.               ( '*' fact     { term0.value := term0.value * fact2.value;    }
  234.               | '/' fact     { term0.value := term0.value DIV fact3.value;  }
  235.               ) *
  236.             .
  237.     fact    : const          { fact0.value := const1.value;                 }
  238.             | '(' expr ')'   { fact0.value := expr1.value;                  }
  239.             .
  240.  
  241.  
  242.      The above example specifies the evaluation of simple  arithmetic  expres-
  243. sions  using  one attribute called value.  The operator "*" denotes repetition
  244. zero, once, or more times.  The attributes have to be declared as members of a
  245. record type called tParsAttribute.
  246.  
  247.     Example:
  248.     TYPE tParsAttribute = RECORD value: INTEGER; END;
  249.  
  250.  
  251.  
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.                                                                              3
  264.  
  265.  
  266. 2.2.  Implementation
  267.  
  268.      The implementation of the L-attribution in the generated parsers is  very
  269. simple.  As  usual,  every  nonterminal is analyzed by a procedure. Every pro-
  270. cedure has one (reference) parameter referring to the  left-hand  side  attri-
  271. butes.  As  all  attributes  are declared as members of one (record) type, one
  272. parameter suffices to pass an arbitrary number of attributes.  For all  right-
  273. hand  side  symbols with attributes, local variables are declared.  This solu-
  274. tion provides very efficient stacking of attributes via  the  usual  procedure
  275. call  mechanism.  Stacking is necessary for the attribute evaluation of recur-
  276. sive grammar rules.
  277.  
  278.     Example:
  279.     PROCEDURE expr (...; VAR expr0: tParsAttribute);
  280.     VAR term1, term2, term3, term4: tParsAttribute;
  281.     BEGIN
  282.        ...
  283.     END expr;
  284.  
  285.  
  286. 3.  Non LL(1) Grammars
  287.  
  288.      Sometimes grammars do not obey the LL(1) property. They are said to  con-
  289. tain  LL(1)  conflicts.  A  well-known example is the dangling-else problem of
  290. Pascal: in case of nested it-then-else statements it may not be clear to which
  291. IF  an  ELSE  belongs. It is very easy to solve this conflicts in hand-written
  292. solutions.  Ell handles LL(1) conflicts in the following ways:
  293.  
  294. -    Several alternatives (operator |) cause a conflict if  their  FIRST  sets
  295.      are not disjoint: the alternative given first is selected.
  296.  
  297. -    An optional part (operators [] and *) causes a conflict if its FIRST  set
  298.      is  not  disjoint from its FOLLOW set: the optional part will be analyzed
  299.      because otherwise it would be useless.
  300.  
  301. -    Parts that may be repeated at least once cause a conflict if their  FIRST
  302.      and  FOLLOW sets are not disjoint (as above): the repetition will be con-
  303.      tinued because otherwise it would be executed only once.
  304.  
  305.      With the above rules it can happen that alternatives are never  taken  or
  306. that  it  is  impossible  for a repetition to terminate for any correct input.
  307. These cases as well as left recursion are  considered  to  be  serious  design
  308. faults  in  the  grammar and are reported as errors. Otherwise LL(1) conflicts
  309. are resolved as described above and reported as warnings.
  310.  
  311. 4.  Error Recovery
  312.  
  313. 4.1.  User's View
  314.  
  315.      The generated parsers include information and program code to handle syn-
  316. tax  errors  completely  automatically and provide expressive error reporting,
  317. recovery, and repair. Every incorrect input is "virtually" transformed into  a
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.  
  325.  
  326.  
  327.  
  328.                                                                              4
  329.  
  330.  
  331.  
  332. Source Program:
  333. MODULE test;
  334. BEGIN
  335.    IF (a = ] 1 write (a) END;
  336. END test.
  337. Error Messages:
  338. 3, 12: Error       syntax error
  339. 3, 12: Information expected symbols: Ident Integer Real String '(' '+' '-' '{' 'NOT'
  340. 3, 14: Information restart point
  341. 3, 16: Error       syntax error
  342. 3, 16: Information restart point
  343. 3, 16: Repair      symbol inserted : ')'
  344. 3, 16: Repair      symbol inserted : 'THEN'
  345.  
  346. Fig. 1: Example of Automatic Error Messages
  347.  
  348. syntactically correct  program  with  the  consequence  of  executing  only  a
  349. "correct"  sequence  of  semantic  actions.   Therefore the following compiler
  350. phases like semantic analysis don't have to bother  with  syntax  errors.  Ell
  351. provides  a prototype error module which prints messages as shown in Figure 1.
  352. Appendix 4 contains a  larger  example  demonstrating  the  behaviour  of  our
  353. method.  Internally the error recovery works as follows:
  354.  
  355. - The location of the syntax error is reported.
  356.  
  357. - If possible, the tokens that would be a legal continuation  of  the  program
  358.   are reported.
  359.  
  360. - The tokens that can serve  to  continue  parsing  are  computed.  A  minimal
  361.   sequence of tokens is skipped until one of these tokens is found.
  362.  
  363. - The recovery location (restart point) is reported.
  364.  
  365. - Parsing continues in the so-called repair mode.  In  this  mode  the  parser
  366.   behaves  as  usual  except that no tokens are read from the input. Instead a
  367.   minimal sequence of tokens is synthesized to repair the error.   The  parser
  368.   stays  in  this mode until the input token can be accepted.  The synthesized
  369.   tokens are reported as inserted symbols.  The program  can  be  regarded  as
  370.   repaired,  if the skipped tokens are replaced by the synthesized ones.  Upon
  371.   leaving repair mode, parsing continues as usual.
  372.  
  373. 4.2.  Implementation
  374.  
  375.      During LL(1) analysis the following kinds of syntax errors can occur:  at
  376. the  analysis  of  a  terminal the current (look-ahead) token can be different
  377. from the expected terminal.  At the analysis of alternatives the current token
  378. can  be  member  of  none  of the FIRST sets of the possible branches.  At the
  379. analysis of optional or iterated parts the current token can be member of nei-
  380. ther  the  FIRST set nor the FOLLOW set of the construct.  In order to achieve
  381. good quality error recovery, the latter test has to be  performed  before  the
  382.  
  383.  
  384.  
  385.  
  386.  
  387.  
  388.  
  389.  
  390.  
  391.  
  392.                                                                              5
  393.  
  394.  
  395. analysis  of  an  optional  part  and  before  every  iteration.  This section
  396. discusses the interesting aspects of error recovery: how the sets of  expected
  397. tokens  are  defined,  how parsing is continued after syntax errors, how error
  398. repair works, and how an efficient implementation is achieved.
  399.  
  400. 4.2.1.  Expected Symbols
  401.  
  402.      At the location of a syntax error, we try to report the set  of  expected
  403. tokens.   To be able to report the exact set of expected tokens, syntax errors
  404. have to be detected as early as possible.  Furthermore,  information  must  be
  405. maintained  during  parsing  because the exact sets depend on the dynamic call
  406. hierarchy. Early detection of errors requires the knowledge of the exact  FOL-
  407. LOW  sets  which  also  depend  on the call hierarchy.  In order to gain effi-
  408. ciency, we report only the subset of the expected tokens which can be computed
  409. at generation time.  This set is necessary for every potential error location.
  410. In case of a terminal the expected token is just this  terminal.  In  case  of
  411. alternatives  the set of expected tokens is the union of the FIRST sets of the
  412. alternatives.  In case of optional or  iterated  parts  the  set  of  expected
  413. tokens  is  the union of the FIRST set and of the local FOLLOW set of the con-
  414. struct.  All the sets are computed at generation time and stored in  the  gen-
  415. erated parsers.
  416.  
  417. 4.2.2.  Recovery Sets
  418.  
  419.      For every possible syntax error a so-called recovery  set  is  determined
  420. containing the tokens where parsing can continue. We present the definition of
  421. recovery sets for plain  BNF,  first.   Appendix  3  shows  the  extension  to
  422. extended  BNF using an attribute grammar formalism.  In case of a syntax error
  423. the situation is as follows (see Fig. 2):
  424.  
  425. -    Analysis of the productions p1, . . . , pn has started.
  426.  
  427. -    Every production pi is processed by a procedure called Xi.
  428.  
  429. -    Every procedure Xi calls a procedure Xi+1 to analyze a nonterminal of the
  430.      right-hand side.
  431.  
  432. -    Procedure Xn  detects an error at position Z.
  433.  
  434. -    The current call hierarchy is X1, . . . , Xn.
  435.  
  436.  
  437.  
  438.  
  439.  
  440.  
  441.  
  442.  
  443.  
  444.  
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.                                                                              6
  458.  
  459.  
  460.  
  461.   p1:     X1     ->   Y11      ...               X2   Y1j1        ...   Y1n1
  462.   p2:     X2     ->   Y21      ...               X3   Y2j2        ...   Y2n2
  463.   ...
  464.   pn-1:   Xn-1   ->   Yn-1,1   ...   Yn-1,in-1   Xn   Yn-1,jn-1   ...   Yn-1,nn-1
  465.   pn:     Xn     ->   Yn1      ...   Ynin        Z    Ynjn        ...   Ynnn
  466.  
  467.  
  468.       Fig. 2: Situation in case of a syntax error (pi  P, Xi  N, Yij  V, Z V)
  469.  
  470.  
  471.      Let us concentrate first on  the  situation  locally  in  production  pn.
  472. Parsing  could  continue at the symbols Ynjn, . . . , Ynnn.  The set of tokens
  473. that allow to continue parsing, or in other words the local recovery set Rn is
  474. therefore the union of the FIRST sets of Ynjn, . . . , Ynnn:
  475.  
  476.  
  477.                              Rn = k=jnUnn  FIRST (Ynk)
  478.  
  479.  
  480.  
  481.      However, in general there may be no token  behind  the  location  of  the
  482. error which is member of the local recovery set Rn. Therefore, the productions
  483. pn-1, . . . , p1 have to be taken into account, too.  Parsing can continue  at
  484. all symbols not analyzed yet:
  485.  
  486.  
  487.                          Yn-1,jn-1   ...   Yn-1,nn-1
  488.                                      ...
  489.                          Y2j2        ...   Y2n2
  490.                          Y1j1        ...   Y1n1
  491.  
  492.  
  493. Therefore in general we need all local recovery sets Ri:
  494.  
  495.  
  496.                   Ri = k=jiUni  FIRST (Yik)     i = 1, . . . , n
  497.  
  498.  
  499. The global recovery set R is the union of all involved local recovery sets:
  500.  
  501.  
  502.                                   R = i=1Un  Ri
  503.  
  504.  
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.                                                                              7
  517.  
  518.  
  519.      The global recovery set R is used to stop skipping of tokens. Tokens  are
  520. skipped  until  one  is reached that is member of R. This location is called a
  521. restart point. This method of recovery terminates because only symbols not yet
  522. analyzed  are considered as potential restart points. Their processing is con-
  523. tained in the current call hierarchy.
  524.  
  525.      In extreme cases the complete rest of the input is skipped. To  guarantee
  526. termination  of  skipping,  a  special  token for end of file (sEof) has to be
  527. member of R.  This is assured by augmenting the grammar by the following rule:
  528.  
  529.                               p0: X0 -> X1 sEof
  530.  
  531. where X1 is the original and X0 is the new start symbol of the grammar.
  532.  
  533.      The definition of R can be modified in some ways. First, if the symbol  Z
  534. is  a  terminal  it  can be included in R in order to improve the behaviour of
  535. error recovery in case of  superfluous  tokens.  Second,  it  is  possible  to
  536. exclude  some  elements  (except  sEof) from R without causing the recovery to
  537. fail. However, its behaviour  becomes  more  coarse  because  eventually  more
  538. tokens  are skipped. If only sEof remains in R, we arrive at panic mode: after
  539. skipping the rest of the input no more errors can be found.
  540.  
  541.      The computation of the global recovery set R can consume  a  considerable
  542. amount  of  run  time. For efficiency reasons, we found the following solution
  543. quite satisfactory: The local recovery sets  can  be  computed  at  generation
  544. time.  For  every right-hand side symbol (or position) a local recovery set is
  545. computed and stored.  The global recovery set has to be computed at run  time,
  546. as  it  depends on the current call hierarchy. The union of the local recovery
  547. sets is computed only in the case of an error. As long as there is  no  error,
  548. it  suffices  to  maintain  a simple data structure that allows to compute the
  549. union on demand.
  550.  
  551.      If the local recovery sets are stored in an array of sets it is enough to
  552. know  the  index of a set in this array. The global restart set is represented
  553. as a list of such indices. This list allows to effectively compute the  global
  554. restart set whenever needed.
  555.  
  556.      A list representing the global restart set is passed via a second parame-
  557. ter  to  every procedure. In case of an error, the local recovery set is added
  558. to the list and then the  real  set  union  is  accomplished.  Before  calling
  559. another procedure to analyze a nonterminal, the list is extended by a suitable
  560. element. The list elements are declared as local variable of  the  procedures.
  561. This way, allocation and deallocation of storage is for free.  For details see
  562. Fig. 3 as well as Appendix 1.
  563.  
  564. 4.2.3.  Error Repair
  565.  
  566.      Every incorrect program is repaired (or transformed) into a syntactically
  567. correct  program. Error repair is accomplished by skipping tokens as described
  568. above and by imaginary inserting tokens. This insertion is realized relatively
  569. easy by continuing parsing as nothing would have happened. Whenever a terminal
  570. is expected which is different from the current input token, it is reported as
  571. inserted.  Whenever  alternatives  are analyzed and the current input token is
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.                                                                              8
  583.  
  584.  
  585.  
  586. TYPE tUnionPtr  = POINTER TO tUnion;
  587. TYPE tUnion     = RECORD             (* type for list elements             *)
  588.                     GlobalRecoverySet   : tUnionPtr;
  589.                     LocalRecoverySet    : SHORTCARD;
  590.                   END;
  591.                                      (* procedure for a nonterminal        *)
  592. PROCEDURE Module (GlobalRecoverySet: tUnionPtr; VAR Module0: tParsAttribute);
  593. VAR
  594.   Union : tUnion;
  595.   Ident1: tScanAttribute;
  596.   Block1: tParsAttribute;
  597.   ...
  598. BEGIN
  599.   Union.GlobalRecoverySet := GlobalRecoverySet;
  600.   IF Token # sMODULE THEN            (* analysis of a literal              *)
  601.     RecoveryLiteral (sMODULE, 138, GlobalRecoverySet);
  602.   ELSE
  603.     Token := GetToken (); IsRepairMode := FALSE;
  604.   END;
  605.   IF Token # sIdent THEN             (* analysis of an attributed terminal *)
  606.     RecoveryTerminal (sIdent, 138, GlobalRecoverySet, Ident1);
  607.   ELSE
  608.     Ident1 := Attribute;             (* receive attribute from scanner     *)
  609.     Token := GetToken (); IsRepairMode := FALSE;
  610.   END;
  611.   Union.LocalRecoverySet := 57;      (* analysis of a nonterminal          *)
  612.   Block (SYSTEM.ADR (Union), Block1);
  613. END Module;
  614.  
  615. Fig. 3: Scheme of the Code for Error Recovery
  616.  
  617. member of none of the possible FIRST sets, an arbitrary branch, which is  non-
  618. recursive, is selected. The restriction to non-recursive branches is necessary
  619. to guarantee termination. There always exists a non-recursive branch  as  long
  620. as  the  grammar is reduced. Semantic actions are executed during error repair
  621. as usual.
  622.  
  623.      Error recovery and error repair is combined in mainly three procedures to
  624. handle  literals, terminals, and alternatives or EBNF constructs, respectively
  625. (see Appendix 2).  To avoid superfluous error messages the  parser  knows  two
  626. modes. In normal mode errors are reported, tokens are skipped, and the mode is
  627. changed to repair mode. In repair mode neither errors are reported nor  tokens
  628. are  skipped.  Instead  the inserted tokens are reported. The mode is switched
  629. back to normal when the analysis of  a  terminal  is  successful.  Then  error
  630. recovery is finished and parsing continues normally.
  631.  
  632.      The local variable Union is a list element for the representation of  the
  633. global  recovery  set.  It suffices to append this element to the list once in
  634. every procedure. Before calling another procedure to analyze a nonterminal the
  635. index  of a local recovery set is assigned to the list element (here: 57). The
  636.  
  637.  
  638.  
  639.  
  640.  
  641.  
  642.  
  643.  
  644.  
  645.  
  646.                                                                              9
  647.  
  648.  
  649. extended list is passed as  parameter.   The  procedures  for  error  recovery
  650. called  ErrorRecovery,  RecoveryTerminal,  and  RecoveryLiteral  are  given in
  651. Appendix 2. They are called only in case of syntax errors.  As errors are con-
  652. sidered  to  be a rare event, these procedures do not have to care about effi-
  653. ciency. Their parameters  describe  the  expected  token  (here:  sMODULE  and
  654. sIdent),  the  index  of  the  local  recovery set (here: 138), and the global
  655. recovery set. For attributed terminals a fourth parameter specifies the  vari-
  656. able  receiving  the attributes from the scanner. The interface to the scanner
  657. consists primarily of the following objects: the  procedure  GetToken  returns
  658. the next input token, the global variable Attribute contains the attributes of
  659. the current token, and the procedure ErrorAttribute (see Appendix 2) is called
  660. by the parser to get attribute values for synthesized tokens.
  661.  
  662. 5.  Related Work
  663.  
  664.      Much work has been published about error recovery. We limit this  discus-
  665. sion  on  giving  reasons  for our solution and mention a few similar methods.
  666. The design of our error recovery was guided by the following requirements:
  667.  
  668. -    automatic derivation of error recovery from the grammar
  669.  
  670. -    efficient parsing in terms of run time
  671.  
  672. -    provision of error repair
  673.  
  674.      Efficiency implies a backtrack-free strategy and asks for a clever imple-
  675. mentation.   Automatic  derivation and a backtrack-free strategy imply more or
  676. less the definition of recovery sets as given above.
  677.  
  678.      We consider error repair to be important because  error  recovery  should
  679. not  consider  syntactical  aspects  only. Syntax analysis is usually combined
  680. with semantic analysis.  Although error repair might not  transform  an  error
  681. the  way the programmer originally intended, it does transform every erroneous
  682. program into a syntactically correct one with the consequence that  only  syn-
  683. tactically  correct  information  is  passed to semantic analysis. This allows
  684. great simplifications in the latter because it does not  have  to  care  about
  685. syntax errors.
  686.  
  687.      The definition for the recovery sets given above is  not  new  as  it  is
  688. somehow  inherent in the problem. Similar definitions have been presented pre-
  689. viously e. g.  by  [Iro63,  ReM85,  SMM84].   Hand-written  recursive  descent
  690. parsers usually implement similar designs [Wir86]. The advantages of our solu-
  691. tion are the provision of error repair and  the  efficient  implementation  of
  692. error recovery. The compiler generators Coco [ReM85] and Coco/R [M\90] use the
  693. same strategy for error recovery but do not provide error  repair.  The  effi-
  694. ciency  of  the parsers generated by Coco/R is comparable to the efficiency of
  695. our method.  Using Coco/R, error recovery has to be tailored by giving  simple
  696. directives.   Ell  does  not  require  any  user engagement and produces error
  697. recovery automatically. Appendix  4  presents  the  behaviour  of  our  method
  698. applied  to  the  example  program used in [M\90].  The rather long listing of
  699. messages is the direct output of the information provided by the  parser.  The
  700. final layout of the messages can be easily adapted to the ideas of the user.
  701.  
  702.  
  703.  
  704.  
  705.  
  706.  
  707.  
  708.  
  709.  
  710.  
  711.                                                                             10
  712.  
  713.  
  714. 6.  Implementation Issues
  715.  
  716.      Parsing is implemented using recursive procedures as outlined above.  The
  717. operators  of  extended BNF are mapped to statements of the target language as
  718. given in Appendix 1.  In obvious cases simple optimizations are exploited. For
  719. example the checks for terminals and literals can be omitted in some cases.
  720.  
  721.      More  sophisticated  implementation  decisions  concern  the  CASE/switch
  722. statements  and  the test for set membership. If C is used as target language,
  723. it turned out that many C compilers optimize switch statements  in  favour  of
  724. storage. If the set of case labels is non-compact, a sorted list of values and
  725. addresses is generated. A run time system routine performs  binary  search  in
  726. this  table  in order to map the current switch value to the address of a case
  727. branch.  To trick the C compiler, Ell inserts dummy labels to make the set  of
  728. case labels compact. Then the C compilers use a jump table which executes con-
  729. siderably faster. Generating compact sets of case labels improved the over all
  730. run time of the parsers by 30%.
  731.  
  732.      Sets are implemented as bit vectors if they contain more  than  one  ele-
  733. ment.   In general an array of memory words is needed to store the bits of one
  734. set (see Fig. 4). The membership test would be coded as follows:
  735.  
  736.     Seti: ARRAY [0..k] OF BITSET;
  737.     e  Seti   _   (e MOD 32) IN Seti [e DIV 32]
  738.  
  739. On a MC 68020 processor this produces the following six  machine  instructions
  740. including two divide instructions:
  741.  
  742.             movl    _e:l,d1
  743.             divsll  #0x20,d2:d1
  744.             movl    _e:l,d3
  745.             divsl   #0x20,d3
  746.             movl    (_Set:l,d3:w:4),d3
  747.             btst    d2,d3
  748.  
  749.  
  750.      It is much more advantageous to store the sets vertically instead of hor-
  751. izontally  as above. 32 sets can be stored side by side in a sufficient number
  752. of words (see Fig. 5). The membership test is coded as follows:
  753.  
  754.  
  755.  
  756.  
  757.  
  758.                             Fig. 4: horizontal set
  759.  
  760.  
  761.  
  762.                              Fig. 5: vertical set
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.  
  773.  
  774.  
  775.                                                                             11
  776.  
  777.  
  778.     Set: ARRAY [0..m] OF BITSET;
  779.     e  Seti   _   i IN Set [e]
  780.  
  781. Note, that i is a constant. Now on a MC 68020 processor two  machine  instruc-
  782. tions suffice:
  783.  
  784.             movw    _e+2:l,d4
  785.             btst    #4,(_Set:l,d4:w:4)
  786.  
  787. If the global variables e and Set could be stored in registers, the membership
  788. test could be done even with one machine instruction, only:
  789.  
  790.             btst    #4,a0@(0,d0:l)
  791.  
  792. This implementation of the membership test has been  previously  described  in
  793. [Gra88b].  In  our  case,  the  two  ways of the membership test account for a
  794. difference of another 30% in the over all run time of the parser.
  795.  
  796. 7.  Summary
  797.  
  798.      We presented the interesting features of  the  recursive  descent  parser
  799. generator  Ell  from  the  user's  point  of  view and from the implementation
  800. aspects.  The outstanding features of Ell are the L-attribution mechanism, its
  801. ability  to handle non LL(1) grammars, and its automatic and comfortable error
  802. handling, which includes error reporting, recovery, and repair.
  803.  
  804.      The generated parsers are extremely efficient in terms of run  time.  For
  805. example  a  Modula-2  parser  runs  at  a speed of 55,000 tokens per second or
  806. 900,000 lines per minute on a MC 68020  processor  (excluding  scanning).  The
  807. size  of  the  parser  is  25  K bytes and the run time of the generator is 10
  808. seconds.
  809.  
  810. Acknowledgements
  811.  
  812.      Whereas the author designed the generated code and  the  error  recovery,
  813. the generator Ell was programmed by D. Kuske. The implementation of the global
  814. recovery sets was discovered independently  by  D.  Schwartz-Hertzner  and  D.
  815. Kuske.  B.  Vielsack added the generation of C code, the L-attribution mechan-
  816. ism, and the disambiguating rules for non LL(1) grammars.  The  implementation
  817. of the set membership test is due to W. M. Waite and B. Gray.
  818.  
  819. References
  820.  
  821. [DuW81]
  822.      R. C. Dunn and W. M. Waite, SYNPUT, Department of Electrical Engineering,
  823.      Univ. of Colorado, Boulder, CO, Feb. 1981.
  824.  
  825. [Gra88a]
  826.      R. W. Gray, Automatic Error Recovery in  a  Fast  Parser,  Summer  USENIX
  827.      Conference, , 1988.
  828.  
  829.  
  830.  
  831.  
  832.  
  833.  
  834.  
  835.  
  836.  
  837.  
  838.  
  839.  
  840.                                                                             12
  841.  
  842.  
  843. [Gra88b]
  844.      R. W. Gray, \-GLA - A Generator for Lexical  Analyzers  That  Programmers
  845.      can Use, University of Colorado, Boulder, 1988.
  846.  
  847. [Gro88]
  848.      J. Grosch, Generators for High-Speed Front-Ends, LNCS 371,  (Oct.  1988),
  849.      81-92, Springer Verlag.
  850.  
  851. [GrV88]
  852.      J. Grosch and B. Vielsack, The Parser Generators Lalr and  Ell,  Compiler
  853.      Generation   Report  No.  8,  GMD  Forschungsstelle  an  der  Universitat
  854.      Karlsruhe, Apr. 1988.
  855.  
  856. [Iro63]
  857.      E. T. Irons, An Error Correcting Parse Algorithm, Comm. ACM 6,  11  (Nov.
  858.      1963), 669-673.
  859.  
  860. [M90]
  861.      H. Mossenbock, Coco/R - A Generator for Fast Compiler Front-Ends,  Report
  862.      No. 127, Departement Informatik, ETH Zurich, Feb. 1990.
  863.  
  864. [ReM85]
  865.      P.   Rechenberg   and   H.   Mossenbock,   Ein   Compiler-Generator   fur
  866.      Mikrocomputer, Hanser, Munchen, 1985.
  867.  
  868. [SMM84]
  869.      M. Spenke, H. Muhlenbein, M. Mevenkamp, F.  Mattern  and  C.  Beilken,  A
  870.      Language  Independent  Error Recovery Method for LL(1) Parsers, Software-
  871.      Practice & Experience 14, 11 (Nov. 1984), 1095-1107.
  872.  
  873. [Wil79]
  874.      R. Wilhelm, Attributierte Grammatiken, Informatik Spektrum 2,  3  (1979),
  875.      123-130.
  876.  
  877. [Wir86]
  878.      N. Wirth, Compilerbau, Teubner, Stuttgart, 1986.
  879.  
  880.  
  881.  
  882.  
  883.  
  884.  
  885.  
  886.  
  887.  
  888.  
  889.  
  890.  
  891.  
  892.  
  893.  
  894.  
  895.  
  896.  
  897.  
  898.  
  899.  
  900.  
  901.  
  902.  
  903.  
  904.  
  905.                                                                             13
  906.  
  907.  
  908. Appendix 1: Scheme of the Code Generated for EBNF Constructs
  909.  
  910.  
  911.                                 (* Literal t                            *)
  912. IF Token # t THEN RecoveryLiteral (t, Recover(t), GlobalrecoverySet);
  913. ELSE Token := GetToken (); IsRepairMode := FALSE;
  914. END;
  915.                                 (* Terminal t (with attribute ti)       *)
  916. IF Token # t THEN RecoveryTerminal (t, Recover(t), GlobalrecoverySet, ti);
  917. ELSE ti := Attribute; Token := GetToken (); IsRepairMode := FALSE;
  918. END;
  919.                                 (* Nonterminal X (with attribute Xi)    *)
  920. Union.LocalRecoverySet := Recover(X); X (SXSTEM.ADR (Union), Xi);
  921.  
  922. LOOP                            (* Optional part X = [ Y ]              *)
  923.   IF Token  FIRST (Y) THEN <Y> EXIT;
  924.   ELSIF Token  FOLLOW (X) OR IsRepairMode THEN EXIT; END;
  925.   ErrorRecovery (Expected (X), Recover (X), GlobalRecoverySet);
  926. END;
  927.  
  928. LOOP                            (* Iteration X = Y * or X = { Y }       *)
  929.   IF Token  FIRST (Y) THEN <Y>
  930.   ELSIF Token  FOLLOW (X) OR IsRepairMode THEN EXIT;
  931.   ELSE ErrorRecovery (Expected (X), Recover (X), GlobalRecoverySet);
  932.   END;
  933. END;
  934.  
  935. LOOP                            (* Iteration X = Y + or X = Y { Y }     *)
  936.   <Y>
  937.   IF Token  FIRST (Y) THEN
  938.     IF Token  FOLLOW (X) THEN EXIT; END;
  939.     ErrorRecovery (Expected (X), Recover (X), GlobalRecoverySet);
  940.     IF Token  FIRST (Y) THEN EXIT; END;
  941.   END;
  942. END;
  943.  
  944. LOOP                            (* Iteration X = Y || Z or X = Y { Z Y } *)
  945.   <Y>
  946.   IF Token  FIRST (Z) THEN
  947.     IF Token  FOLLOW (X) THEN EXIT; END;
  948.     ErrorRecovery (Expected (X), Recover (X), GlobalRecoverySet);
  949.     IF Token  (FIRST (Y) U FIRST (Z)) THEN EXIT; END;
  950.   END;
  951.   <Z>
  952. END;
  953.  
  954. LOOP                            (* Alternative = X = Y1 | ... | Yn      *)
  955.   CASE Token OF
  956.   | FIRST (Y1) & FOLLOW (Y1): <Y1> EXIT;
  957.       ...
  958.   | FIRST (Yn) & FOLLOW (Yn): <Yn> EXIT;
  959.   ELSE          (* Yd (1 <= d <= n): default alternative for error case *)
  960.                 (*                   duplication of one of the above    *)
  961.  
  962.  
  963.  
  964.  
  965.  
  966.  
  967.  
  968.  
  969.  
  970.  
  971.                                                                             14
  972.  
  973.  
  974.     IF IsRepairMode THEN <Yd> EXIT; END;
  975.     ErrorRecovery (Expected (X), Recover (X), GlobalRecoverySet);
  976.   END;
  977. END;
  978.  
  979.  
  980.  
  981.  
  982.  
  983.  
  984.  
  985.  
  986.  
  987.  
  988.  
  989.  
  990.  
  991.  
  992.  
  993.  
  994.  
  995.  
  996.  
  997.  
  998.  
  999.  
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005.  
  1006.  
  1007.  
  1008.  
  1009.  
  1010.  
  1011.  
  1012.  
  1013.  
  1014.  
  1015.  
  1016.  
  1017.  
  1018.  
  1019.  
  1020.  
  1021.  
  1022.  
  1023.  
  1024.  
  1025.  
  1026.  
  1027.  
  1028.  
  1029.  
  1030.  
  1031.  
  1032.  
  1033.  
  1034.  
  1035.  
  1036.                                                                             15
  1037.  
  1038.  
  1039. Appendix 2: Procedures for Error Recovery
  1040.  
  1041.  
  1042. TYPE tUnionPtr  = POINTER TO tUnion;
  1043. TYPE tUnion     = RECORD                    (* type for list elements      *)
  1044.                     GlobalRecoverySet   : tUnionPtr;
  1045.                     LocalRecoverySet    : SHORTCARD;
  1046.                   END;
  1047. TYPE tSet       = ARRAY [0..Upb] OF BITSET; (* type for bit sets           *)
  1048.  
  1049. VAR SetMemory   : ARRAY [0..169] OF tSet;   (* storage for horizontal sets *)
  1050.  
  1051.                                             (* test for set membership     *)
  1052. PROCEDURE IsElement (Set: tSet; Element: SHORTCARD): BOOLEAN;
  1053.   BEGIN
  1054.     RETURN Element MOD BitsPerBitset IN Set [Element DIV BitsPerBitset];
  1055.   END IsElement;
  1056.                             (* compute global recovery set and skip tokens *)
  1057. PROCEDURE SkipTokens (LocalRecoverySet: SHORTCARD; GlobalRecoverySet: tUnionPtr);
  1058.   VAR RecoverySet: tSet; i: SHORTCARD;
  1059.   BEGIN
  1060.     RecoverySet := SetMemory [LocalRecoverySet];
  1061.     INCL (RecoverySet [0], sEof);
  1062.     WHILE GlobalRecoverySet # NIL DO
  1063.       FOR i := 0 TO Upb DO RecoverySet [i] :=
  1064.         RecoverySet [i] + SetMemory [GlobalRecoverySet^.LocalRecoverySet] [i];
  1065.       END;
  1066.       GlobalRecoverySet := GlobalRecoverySet^.GlobalRecoverySet;
  1067.     END;
  1068.     WHILE NOT IsElement (RecoverySet, Token) DO
  1069.       Token := GetToken ();
  1070.     END;
  1071.     ErrorMessage (RestartPoint, Information, Line, Column);
  1072.     IsRepairMode := TRUE;
  1073.   END SkipTokens;
  1074.  
  1075. PROCEDURE ErrorRecovery (ExpectedSet      : SHORTCARD;
  1076.                          LocalRecoverySet : SHORTCARD;
  1077.                          GlobalRecoverySet: tUnionPtr);
  1078.   BEGIN
  1079.     IF NOT IsRepairMode THEN
  1080.       INC (ErrorCount);
  1081.       ErrorMessage (SyntaxError, Error, Line, Column);
  1082.       ErrorMessageI (ExpectedSymbols, Information, Line, Column, TokenSet,
  1083.                      SYSTEM.ADR (SetMemory [ExpectedSet]));
  1084.       SkipTokens (LocalRecoverySet, GlobalRecoverySet);
  1085.     END;
  1086.   END ErrorRecovery;
  1087.  
  1088.  
  1089.  
  1090.  
  1091.  
  1092.  
  1093.  
  1094.  
  1095.  
  1096.  
  1097.  
  1098.  
  1099.  
  1100.  
  1101.                                                                             16
  1102.  
  1103.  
  1104. PROCEDURE RecoveryTerminal (Expected         : SHORTCARD;
  1105.                             LocalRecoverySet : SHORTCARD;
  1106.                             GlobalRecoverySet: tUnionPtr;
  1107.                VAR RepairAttribute: tScanAttribute); (* for terminals only *)
  1108.   BEGIN
  1109.     IF NOT IsRepairMode THEN
  1110.       INC (ErrorCount);
  1111.       ErrorMessage (SyntaxError, Error, Line, Column);
  1112.       ErrorMessageI (ExpectedSymbols, Information, Line, Column, Symbol,
  1113.                      SYSTEM.ADR (Expected));
  1114.       SkipTokens (LocalRecoverySet, GlobalRecoverySet);
  1115.     END;
  1116.     IF Token # Expected THEN
  1117.       ErrorMessageI (SymbolInserted, Repair, Line, Column, Symbol,
  1118.                      SYSTEM.ADR (Expected));
  1119.       ErrorAttribute (Expected, RepairAttribute);    (* for terminals only *)
  1120.     ELSE
  1121.       RepairAttribute := Attribute;                  (* for terminals only *)
  1122.       IF Token # sEof THEN Token := GetToken (); END;
  1123.       IsRepairMode := FALSE;
  1124.     END;
  1125.   END RecoveryTerminal;
  1126.  
  1127. PROCEDURE ErrorRecoveryLiteral (Expected         : SHORTCARD;
  1128.                                 LocalRecoverySet : SHORTCARD;
  1129.                                 GlobalRecoverySet: tUnionPtr);
  1130.  
  1131.    (* like ErrorRecoveryTerminal except as marked above *)
  1132.  
  1133. Appendix 3: Attribute Grammar to Compute the Recovery Sets
  1134.  
  1135.  
  1136. N           nonterminal
  1137. E           expression
  1138. t           terminal or literal
  1139. R           recovery set
  1140. In, Out     temporary attributes
  1141.  
  1142.  
  1143. N = E           {E.In := ;                                       }
  1144. E = t           {E.R := FIRST (t) U E.In; E.Out := E.R;          }
  1145. E = N           {E.R := E.In; E.Out := FIRST (N) U E.In;         }
  1146. E = [ E1 ]      {E1.In := E.In; E.R := FIRST (E1) U E.In; E.Out := E.R;}
  1147. E = E1 *        {E1.In := E.In; E.R := FIRST (E1) U E.In; E.Out := E.R;}
  1148. E = E1 +        {E1.In := E.In; E.R := FIRST (E1) U E.In; E.Out := E.R;}
  1149. E = E1 || E2    {E1.In := FIRST (E2) U E.In; E2.In := FIRST (E1) U E.In;
  1150.                  E.R := FIRST (E1) U FIRST (E2) U E.In; E.Out := E.R;}
  1151. E = E1 |...| En {E1.In := E.In; ... En.In := E.In;
  1152.                  E.R := FIRST (E1) U ... U FIRST (En) U E.In; E.Out := E.R;}
  1153. E = E1 E2       {E2.In := E.In; E1.In := E2.Out; E.R := E1.Out; E.Out := E.R;}
  1154.  
  1155.  
  1156.  
  1157.  
  1158.  
  1159.  
  1160.  
  1161.  
  1162.  
  1163.  
  1164.  
  1165.  
  1166.                                                                             17
  1167.  
  1168.  
  1169. Appendix 4: Example of Error Recovery
  1170.  
  1171.  
  1172. Source Program:
  1173.     1   MODULE Error;
  1174.     2   CONST M := 10, N = 100  X = 10;
  1175.     3   VAR , a, b, c;
  1176.     4
  1177.     5   PROCEDURE P;
  1178.     6   BEGIN
  1179.     7     s := 0; a = 5 * (b - 1 END;
  1180.     8
  1181.     9   BEGIN
  1182.    10     > a > b;
  1183.    11     WHILE a DO
  1184.    12       BEGIN > b; - c := 0;
  1185.    13       WHILE a > 0 BEGIN
  1186.    14         IF ODD a c := c * - b;
  1187.    15         b := 2 * b a := a /2
  1188.    16       END;
  1189.    17       P := 0; P; 666;
  1190.    18     END .
  1191. Error Messages:
  1192. 2, 9:   Error       syntax error
  1193. 2, 9:   Information expected symbols: '='
  1194. 2, 12:  Information restart point
  1195. 2, 12:  Repair      symbol inserted : '='
  1196. 2, 14:  Error       syntax error
  1197. 2, 14:  Information expected symbols: ';'
  1198. 2, 16:  Information restart point
  1199. 2, 16:  Repair      symbol inserted : ';'
  1200. 2, 25:  Error       syntax error
  1201. 2, 25:  Information restart point
  1202. 2, 25:  Repair      symbol inserted : ';'
  1203. 3, 5:   Error       syntax error
  1204. 3, 7:   Information restart point
  1205. 3, 14:  Error       syntax error
  1206. 3, 14:  Information expected symbols: ':'
  1207. 3, 14:  Information restart point
  1208. 3, 14:  Repair      symbol inserted : ':'
  1209. 3, 14:  Repair      symbol inserted : Ident
  1210. 7, 13:  Error       syntax error
  1211. 7, 13:  Information expected symbols: '(' ':=' ';'
  1212. 7, 19:  Information restart point
  1213. 7, 26:  Error       syntax error
  1214. 7, 26:  Information restart point
  1215. 7, 26:  Repair      symbol inserted : ')'
  1216. 7, 29:  Error       syntax error
  1217. 7, 29:  Information expected symbols: Ident
  1218. 7, 29:  Information restart point
  1219. 7, 29:  Repair      symbol inserted : Ident
  1220.  
  1221.  
  1222.  
  1223.  
  1224.  
  1225.  
  1226.  
  1227.  
  1228.  
  1229.  
  1230.                                                                             18
  1231.  
  1232.  
  1233. 10, 3:  Error       syntax error
  1234. 10, 3:  Information expected symbols: Ident ';' 'CASE' 'EXIT' 'FOR' 'IF' 'LOOP'
  1235.                                       'REPEAT' 'RETURN' 'WHILE' 'WITH'
  1236. 10, 5:  Information restart point
  1237. 10, 7:  Error       syntax error
  1238. 10, 7:  Information expected symbols: '(' ':=' ';'
  1239. 10, 9:  Information restart point
  1240. 10, 9:  Repair      symbol inserted : ';'
  1241. 12, 5:  Error       syntax error
  1242. 12, 5:  Information expected symbols: Ident ';' 'CASE' 'EXIT' 'FOR' 'IF' 'LOOP'
  1243.                                       'REPEAT' 'RETURN' 'WHILE' 'WITH'
  1244. 12, 13: Information restart point
  1245. 12, 16: Error       syntax error
  1246. 12, 16: Information expected symbols: Ident ';' 'CASE' 'EXIT' 'FOR' 'IF' 'LOOP'
  1247.                                       'REPEAT' 'RETURN' 'WHILE' 'WITH'
  1248. 12, 18: Information restart point
  1249. 13, 17: Error       syntax error
  1250. 14, 7:  Information restart point
  1251. 14, 7:  Repair      symbol inserted : 'DO'
  1252. 14, 14: Error       syntax error
  1253. 14, 14: Information restart point
  1254. 14, 14: Repair      symbol inserted : 'THEN'
  1255. 14, 16: Error       syntax error
  1256. 14, 16: Information restart point
  1257. 14, 16: Repair      symbol inserted : ';'
  1258. 14, 25: Error       syntax error
  1259. 14, 25: Information expected symbols: Ident Integer Real String '(' '{' 'NOT'
  1260. 14, 25: Information restart point
  1261. 14, 25: Repair      symbol inserted : Integer
  1262. 15, 18: Error       syntax error
  1263. 15, 18: Information restart point
  1264. 15, 18: Repair      symbol inserted : ';'
  1265. 17, 16: Error       syntax error
  1266. 17, 16: Information expected symbols: Ident ';' 'CASE' 'EXIT' 'FOR' 'IF' 'LOOP'
  1267.                                       'REPEAT' 'RETURN' 'WHILE' 'WITH'
  1268. 17, 19: Information restart point
  1269. 18, 7:  Error       syntax error
  1270. 18, 7:  Information restart point
  1271. 18, 7:  Repair      symbol inserted : 'END'
  1272. 18, 7:  Repair      symbol inserted : 'END'
  1273. 18, 7:  Repair      symbol inserted : Ident
  1274.  
  1275.  
  1276.  
  1277.  
  1278.  
  1279.  
  1280.  
  1281.  
  1282.  
  1283.  
  1284.  
  1285.  
  1286.  
  1287.  
  1288.  
  1289.  
  1290.  
  1291.  
  1292.  
  1293.  
  1294.  
  1295.                                                                              1
  1296.  
  1297.  
  1298. Contents
  1299.  
  1300.         Abstract ........................................................    1
  1301. 1.      Introduction ....................................................    1
  1302. 2.      L-Attribution ...................................................    2
  1303. 2.1.    User's View .....................................................    2
  1304. 2.2.    Implementation ..................................................    3
  1305. 3.      Non LL(1) Grammars ..............................................    3
  1306. 4.      Error Recovery ..................................................    3
  1307. 4.1.    User's View .....................................................    3
  1308. 4.2.    Implementation ..................................................    4
  1309. 4.2.1.  Expected Symbols ................................................    5
  1310. 4.2.2.  Recovery Sets ...................................................    5
  1311. 4.2.3.  Error Repair ....................................................    7
  1312. 5.      Related Work ....................................................    9
  1313. 6.      Implementation Issues ...........................................   10
  1314. 7.      Summary .........................................................   11
  1315.         Acknowledgements ................................................   11
  1316.         References ......................................................   11
  1317.         Appendix 1: Scheme of the Code Generated for  EBNF  Constructs
  1318.      ....................................................................   13
  1319.         Appendix 2: Procedures for Error Recovery .......................   15
  1320.         Appendix 3: Attribute Grammar to Compute the Recovery Sets ......   16
  1321.         Appendix 4: Example of Error Recovery ...........................   17
  1322.  
  1323.  
  1324.  
  1325.  
  1326.  
  1327.  
  1328.  
  1329.  
  1330.  
  1331.  
  1332.  
  1333.  
  1334.  
  1335.  
  1336.  
  1337.  
  1338.  
  1339.  
  1340.  
  1341.  
  1342.  
  1343.  
  1344.  
  1345.  
  1346.  
  1347.  
  1348.  
  1349.  
  1350.  
  1351.  
  1352.  
  1353.  
  1354.  
  1355.  
  1356.